package com.google.appengine.codelab;

import java.io.IOException;
import java.io.PrintWriter;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.HashMap;
import java.util.Map;

import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.appengine.api.datastore.Entity;

@SuppressWarnings("serial")
public class UserStory15Servlet extends HttpServlet 
{
    private static final Logger logger = Logger.getLogger ( UserStory15Servlet.class.getCanonicalName ());
            
    protected void doGet ( HttpServletRequest req, HttpServletResponse resp ) throws ServletException, IOException 
    {
        //MassaDados.load () ;
        
        resp.setContentType ( "text/plain" ) ;

        String byQuery = req.getParameter ( "query" ) ;
        String byCity  = req.getParameter ( "city" ) ;
            
        logger.log ( Level.INFO, "byQuery: " + byQuery ) ;
        logger.log ( Level.INFO, "byCity.: " + byCity ) ;
        
        PrintWriter out = resp.getWriter () ;

        out.println ( this.writeJSON ( consulta ( byQuery, byCity ))) ;
    }

    private Iterable<String> consulta ( String byQuery, String byCity )
    {
        if (( byQuery != null ) && ( byQuery.equals ( "1" )))
        {
        	logger.log ( Level.INFO, "=== listar todas as cidades que tem fraudes ===" ) ;
        	
            Iterable<String> list = this.getFraudesByCity () ;
            return ( list ) ;
        }
        
        if (( byQuery != null ) && ( byQuery.equals ( "2" )) && ( byCity != null ))
        {
            logger.log ( Level.INFO, "=== listar fraudes de uma cidade agrupadas por cep ===" ) ;
        	
            Iterable<String> list = this.getFraudesByZip ( byCity ) ;
            return ( list ) ;
        }
        
        if (( byQuery != null ) && ( byQuery.equals ( "3" )) && ( byCity != null ))
        {
            logger.log ( Level.INFO, "=== listar fraudes de uma cidade agrupadas por cep ===" ) ;
        	
            Iterable<String> list = this.getFraudesByZipDetail ( byCity ) ;
            return ( list ) ;
        }
    
        return ( null );
    }
    
    public Iterable<String> getFraudesByCity () 
    {
        Map<String, Integer> listCity = new HashMap<String, Integer> () ;
                
        ArrayList<String> list = new ArrayList<String> () ;
       
        Iterable<Entity> entitiesFraude = Util.listEntities ( "Fraude", null, null ) ;

        for ( Entity entityF : entitiesFraude )
        {
            String transId = entityF.getProperty ( "trans_id" ).toString () ;
                    
            Iterable<Entity> entitiesTransacao = Util.listEntities ( "Transacao", "trans_id", transId ) ;
                    
            for ( Entity entityT : entitiesTransacao ) 
            {
                String localidadeId = entityT.getProperty ( "localidade_id" ).toString () ;
               
                Entity localidade = Localidade.getSingleLocalidade ( localidadeId ) ;
                
                String szCity    = localidade.getProperty ( "localidade_city" ).toString () ;
                
                if ( listCity.containsKey ( szCity ))
                    listCity.put ( szCity, listCity.get ( szCity ) + 1 );
                else
                    listCity.put ( szCity, 1 );  
            }
        }
        
        logger.log ( Level.INFO, "=== Listar todas as cidades que tem fraudes ===" );
        
        if (( listCity != null ) && ( !listCity.isEmpty ()))
        {
            list.add ( "#cidades" );
    
            for ( Map.Entry<String, Integer> entry : listCity.entrySet ()) 
            {
                list.add ( "localidade_city" );
                list.add ( entry.getKey ());
                list.add ( "city_total" );
                list.add ( entry.getValue ().toString ());
            }
        }
    
        return list;
    }
    
    public Iterable<String> getFraudesByZip ( String byCity ) 
    {
        Map<String, Integer> listZip = new HashMap<String, Integer> () ;
                
        ArrayList<String> list = new ArrayList<String> () ;
       
        Iterable<Entity> entitiesFraude = Util.listEntities ( "Fraude", null, null ) ;

        for ( Entity entityF : entitiesFraude )
        {
            String transId = entityF.getProperty ( "trans_id" ).toString () ;
                    
            Iterable<Entity> entitiesTransacao = Util.listEntities ( "Transacao", "trans_id", transId ) ;
                    
            for ( Entity entityT : entitiesTransacao ) 
            {
                String localidadeId = entityT.getProperty ( "localidade_id" ).toString () ;
               
                Entity localidade = Localidade.getSingleLocalidade ( localidadeId ) ;
                
                String szCity = localidade.getProperty ( "localidade_city" ).toString () ;
                
                if ( byCity.equals ( szCity ))
                {
                	String szZip = localidade.getProperty ( "localidade_zip" ).toString () ;
                	
                	if ( listZip.containsKey ( szZip ))
                        listZip.put ( szZip, listZip.get ( szZip ) + 1 );
                    else
                        listZip.put ( szZip, 1 );  
                }
            }
        }
        
        logger.log ( Level.INFO, "=== Listar fraudes de uma cidade agrupadas por cep ===" );
        
        if (( listZip != null ) && ( !listZip.isEmpty ()))
        {
            list.add ( "#ceps" );
    
            for ( Map.Entry<String, Integer> entry : listZip.entrySet ()) 
            {
                list.add ( "localidade_zip" );
                list.add ( entry.getKey ());
                list.add ( "zip_total" );
                list.add ( entry.getValue ().toString ());
            }
        }
    
        return list;
    }
    
    public Iterable<String> getFraudesByZipDetail ( String byCity ) 
    {
        Map<String, String> listZip = new HashMap<String, String> () ;
                
        ArrayList<String> list = new ArrayList<String> () ;
       
        Iterable<Entity> entitiesFraude = Util.listEntities ( "Fraude", null, null ) ;

        for ( Entity entityF : entitiesFraude )
        {
            String transId = entityF.getProperty ( "trans_id" ).toString () ;
                    
            Iterable<Entity> entitiesTransacao = Util.listEntities ( "Transacao", "trans_id", transId ) ;
                    
            for ( Entity entityT : entitiesTransacao ) 
            {
                String localidadeId = entityT.getProperty ( "localidade_id" ).toString () ;
               
                Entity localidade = Localidade.getSingleLocalidade ( localidadeId ) ;
                
                String szCity = localidade.getProperty ( "localidade_city" ).toString () ;
                
                if ( byCity.equals ( szCity ))
                {
                	String szZip = localidade.getProperty ( "localidade_zip" ).toString () ;
                	
                	String trans_type  = entityT.getProperty ( "trans_type" ).toString () ;
                	String trans_price = entityT.getProperty ( "trans_price" ).toString () ;
                	String trans_data  = entityT.getProperty ( "trans_data" ).toString () ;
                	String trans_hora  = "";//entityT.getProperty ( "trans_hora" ).toString () ;
                	String estab_id    = entityT.getProperty ( "estabelecimento_id" ).toString () ;
                	String fraude_type = entityF.getProperty ( "fraude_type" ).toString () ;
                	String fraude_frmd = entityF.getProperty ( "fraude_forma_deteccao" ).toString () ;
                	
                	if ( listZip.containsKey ( szZip ))
                        listZip.put ( szZip, listZip.get ( szZip ) + ",\n{ \"trans_id\" : \"" + transId + "\", \"trans_type\" : \"" + trans_type + "\", \"trans_price\" : \"" + trans_price + "\"," +
                	                          " \"trans_data\" : \"" + trans_data + "\", \"trans_hora\" : \"" + trans_hora + "\"," +
                	                          " \"estabelecimento_id\" : \"" + estab_id + "\", \"fraude_type\" : \"" + fraude_type + "\"," +
                	                          " \"fraude_forma_deteccao\" : \"" + fraude_frmd + "\" }" ) ;
                    else
                        listZip.put ( szZip, "[ { \"trans_id\" : \"" + transId + "\", \"trans_type\" : \"" + trans_type + "\", \"trans_price\" : \"" + trans_price + "\"," +
                	                          " \"trans_data\" : \"" + trans_data + "\", \"trans_hora\" : \"" + trans_hora + "\"," +
                	                          " \"estabelecimento_id\" : \"" + estab_id + "\", \"fraude_type\" : \"" + fraude_type + "\"," +
                	                          " \"fraude_forma_deteccao\" : \"" + fraude_frmd + "\" }" ) ;  
                }
            }
        }
        
        logger.log ( Level.INFO, "=== Listar fraudes de uma cidade agrupadas por cep ===" );
        
        if (( listZip != null ) && ( !listZip.isEmpty ()))
        {
            list.add ( "#detail" );
    
            for ( Map.Entry<String, String> entry : listZip.entrySet ()) 
            {
                list.add ( "localidade_zip" );
                list.add ( entry.getKey ());
                list.add ( "fraudes" );
                list.add ( entry.getValue ().toString () + " ]" );
            }
        }
    
        return list;
    }
    public String writeJSON ( Iterable<String> list ) 
    {
        logger.log ( Level.INFO, "creating JSON format object" ) ;
        
        if (( list == null ) || ( !list.iterator ().hasNext ()))
        {
            return "{ \"data\" : [] }" ;
        }

        StringBuilder sb = new StringBuilder () ;
        
        int i = 0;
        
        sb.append ( "{ " ) ;
        
        for ( Iterator<String> element = list.iterator () ; element.hasNext () ; )
        {
            String key = element.next ();
              
            if ( key.equals ( "#cidades" ))
            {
                if ( i > 0 ) 
                {
                    sb.deleteCharAt ( sb.lastIndexOf ( "," ));
                }
                  
                sb.append ( "\"cidades\" : [ \n" ) ;
                    
                if ( element.hasNext ())
                    key = element.next ();
                else
               	    break;
            }
            else if ( key.equals ( "#ceps" ))
            {
                if ( i > 0 ) 
                {
                    sb.deleteCharAt ( sb.lastIndexOf ( "," ));
                }
                  
                sb.append ( " \"por_cep\" : \n[ " ) ;
                
                if ( element.hasNext ())
                    key = element.next ();
                else
                    break;
            }
            else if ( key.equals ( "#detail" ))
            {
                if ( i > 0 ) 
                {
                    sb.deleteCharAt ( sb.lastIndexOf ( "," ));
                }
                  
                sb.append ( " \"detalhado\" : \n[ " ) ;
                
                if ( element.hasNext ())
                    key = element.next ();
                else
                {
                    break;
                }
            }

            sb.append ( "{ \"" + key             + "\" : \"" + element.next () + "\", " ) ;
            sb.append (   "\"" + element.next () + "\" : "   + element.next () + " }, \n" ) ;

            i += 4 ;
        }
        
        if ( i > 0 ) 
        {
            sb.deleteCharAt ( sb.lastIndexOf ( "," ));
        }
        
        sb.append ( "] }" ) ;
        
        return sb.toString ();
    }
}
